
/******************************************************************************
 * MODULE     : object.cpp
 * DESCRIPTION: Implementation of scheme objects
 * COPYRIGHT  : (C) 1999-2011 Joris van der Hoeven and Massimiliano Gubinelli
 *******************************************************************************
 * This software falls under the GNU general public license version 3 or later.
 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
 ******************************************************************************/

#include "object.hpp"
#include "object_l1.hpp"
#include "object_l2.hpp"
#include "object_l3.hpp"

#include "array.hpp"
#include "list.hpp"
#include "modification.hpp"
#include "patch.hpp"
#include "promise.hpp"
#include "scheme.hpp"
#include "tm_timer.hpp"

/******************************************************************************
 * The object representation class
 ******************************************************************************/

static list<tmscm> destroy_list;
extern tmscm       object_stack;

tmscm_object_rep::tmscm_object_rep (tmscm obj) {
  while (!is_nil (destroy_list)) {
    tmscm handle= destroy_list->item;

    tmscm_set_car (handle, tmscm_null ());
    while (tmscm_is_pair (tmscm_cdr (handle)) &&
           tmscm_is_null (tmscm_cadr (handle)))
      tmscm_set_cdr (handle, tmscm_cddr ((handle)));
    destroy_list= destroy_list->next;
  }
  handle=
      tmscm_cons (tmscm_cons (obj, tmscm_null ()), tmscm_car (object_stack));
  tmscm_set_car (object_stack, handle);
}

tmscm_object_rep::~tmscm_object_rep () {
  // Be careful: can't call Scheme code from this destructor,
  // because the destructor can be called during garbage collection.
  destroy_list= list<tmscm> (handle, destroy_list);
}

/******************************************************************************
 * Routines on objects
 ******************************************************************************/

tm_ostream&
operator<< (tm_ostream& out, object obj) {
  out.flush ();
  if (out == cout) call ("write", obj);
  else if (out == cerr) call ("write-err", obj);
  else TM_FAILED ("not yet implemented");
  call ("force-output");
  return out;
}

bool
operator== (object obj1, object obj2) {
  tmscm o1= object_to_tmscm (obj1), o2= object_to_tmscm (obj2);
  return tmscm_is_equal (o1, o2);
}

bool
operator!= (object obj1, object obj2) {
  return !(obj1 == obj2);
}

int
hash (object obj) {
  return as_int (call ("hash", obj, object (1234567)));
}

/******************************************************************************
 * Utilities
 ******************************************************************************/

object
null_object () {
  return tmscm_to_object (tmscm_null ());
}
object
cons (object obj1, object obj2) {
  return tmscm_to_object (
      tmscm_cons (object_to_tmscm (obj1), object_to_tmscm (obj2)));
}
object
list_object (object obj1) {
  return cons (obj1, null_object ());
}
object
list_object (object obj1, object obj2) {
  return cons (obj1, cons (obj2, null_object ()));
}
object
list_object (object obj1, object obj2, object obj3) {
  return cons (obj1, cons (obj2, cons (obj3, null_object ())));
}
object
symbol_object (string s) {
  return tmscm_to_object (symbol_to_tmscm (s));
}
object
car (object obj) {
  return tmscm_to_object (tmscm_car (object_to_tmscm (obj)));
}
object
cdr (object obj) {
  return tmscm_to_object (tmscm_cdr (object_to_tmscm (obj)));
}
object
caar (object obj) {
  return tmscm_to_object (tmscm_caar (object_to_tmscm (obj)));
}
object
cdar (object obj) {
  return tmscm_to_object (tmscm_cdar (object_to_tmscm (obj)));
}
object
cadr (object obj) {
  return tmscm_to_object (tmscm_cadr (object_to_tmscm (obj)));
}
object
cddr (object obj) {
  return tmscm_to_object (tmscm_cddr (object_to_tmscm (obj)));
}
object
caddr (object obj) {
  return tmscm_to_object (tmscm_caddr (object_to_tmscm (obj)));
}
object
cadddr (object obj) {
  return tmscm_to_object (tmscm_cadddr (object_to_tmscm (obj)));
}

/******************************************************************************
 * Predicates
 ******************************************************************************/

bool
is_null (object obj) {
  return tmscm_is_null (object_to_tmscm (obj));
}
bool
is_list (object obj) {
  return tmscm_is_list (object_to_tmscm (obj));
}
bool
is_bool (object obj) {
  return tmscm_is_bool (object_to_tmscm (obj));
}
bool
is_int (object obj) {
  return tmscm_is_int (object_to_tmscm (obj));
}
bool
is_double (object obj) {
  return tmscm_is_double (object_to_tmscm (obj));
}
bool
is_string (object obj) {
  return tmscm_is_string (object_to_tmscm (obj));
}
bool
is_symbol (object obj) {
  return tmscm_is_symbol (object_to_tmscm (obj));
}
bool
is_path (object obj) {
  return tmscm_is_path (object_to_tmscm (obj));
}
bool
is_array_double (object obj) {
  return tmscm_is_array_double (object_to_tmscm (obj));
}
bool
is_patch (object obj) {
  return tmscm_is_patch (object_to_tmscm (obj));
}
bool
is_modification (object obj) {
  return tmscm_is_modification (object_to_tmscm (obj));
}

/******************************************************************************
 * Basic conversions
 ******************************************************************************/

object::object (tmscm_object_rep* o) : rep (static_cast<object_rep*> (o)) {}
object::object () : rep (tm_new<tmscm_object_rep> (tmscm_null ())) {}
object::object (bool b) : rep (tm_new<tmscm_object_rep> (bool_to_tmscm (b))) {}
object::object (int i) : rep (tm_new<tmscm_object_rep> (int_to_tmscm (i))) {}
object::object (double x)
    : rep (tm_new<tmscm_object_rep> (double_to_tmscm (x))) {}
object::object (const char* s)
    : rep (tm_new<tmscm_object_rep> (string_to_tmscm (string (s)))) {}
object::object (string s)
    : rep (tm_new<tmscm_object_rep> (string_to_tmscm (s))) {}
object::object (tree t) : rep (tm_new<tmscm_object_rep> (tree_to_tmscm (t))) {}
object::object (list<string> l)
    : rep (tm_new<tmscm_object_rep> (list_string_to_tmscm (l))) {}
object::object (list<tree> l)
    : rep (tm_new<tmscm_object_rep> (list_tree_to_tmscm (l))) {}
object::object (path p) : rep (tm_new<tmscm_object_rep> (path_to_tmscm (p))) {}
object::object (url u) : rep (tm_new<tmscm_object_rep> (url_to_tmscm (u))) {}
object::object (array<double> a)
    : rep (tm_new<tmscm_object_rep> (array_double_to_tmscm (a))) {}
object::object (patch m)
    : rep (tm_new<tmscm_object_rep> (patch_to_tmscm (m))) {}
object::object (modification m)
    : rep (tm_new<tmscm_object_rep> (modification_to_tmscm (m))) {}

bool
as_bool (object obj) {
  tmscm b= object_to_tmscm (obj);
  if (!tmscm_is_bool (b)) return false;
  return tmscm_to_bool (b);
}

int
as_int (object obj) {
  tmscm i= object_to_tmscm (obj);
  if (!tmscm_is_int (i)) return 0;
  return tmscm_to_int (i);
}

double
as_double (object obj) {
  tmscm x= object_to_tmscm (obj);
  if (!tmscm_is_double (x)) return 0.0;
  return tmscm_to_double (x);
}

string
as_string (object obj) {
  tmscm s= object_to_tmscm (obj);
  if (!tmscm_is_string (s)) return "";
  return tmscm_to_string (s);
}

string
as_symbol (object obj) {
  tmscm s= object_to_tmscm (obj);
  if (!tmscm_is_symbol (s)) return "";
  return tmscm_to_symbol (s);
}

scheme_tree
as_tmscm_tree (object obj) {
  tmscm t= object_to_tmscm (obj);
  return tmscm_to_scheme_tree (t);
}

list<string>
as_list_string (object obj) {
  tmscm l= object_to_tmscm (obj);
  if (!tmscm_is_list_string (l)) return list<string> ();
  return tmscm_to_list_string (l);
}

list<tree>
as_list_tree (object obj) {
  tmscm l= object_to_tmscm (obj);
  if (!tmscm_is_list_tree (l)) return list<tree> ();
  return tmscm_to_list_tree (l);
}

path
as_path (object obj) {
  tmscm t= object_to_tmscm (obj);
  if (!tmscm_is_path (t)) return path ();
  return tmscm_to_path (t);
}

array<object>
as_array_object (object obj) {
  ASSERT (is_list (obj), "list expected");
  array<object> ret;
  while (!is_null (obj)) {
    ret << car (obj);
    obj= cdr (obj);
  }
  return ret;
}

array<double>
as_array_double (object obj) {
  ASSERT (is_array_double (obj), "array<double> expected");
  tmscm t= object_to_tmscm (obj);
  return tmscm_to_array_double (t);
}

modification
as_modification (object obj) {
  tmscm m= object_to_tmscm (obj);
  if (!tmscm_is_modification (m)) return mod_assign (path (), "");
  return tmscm_to_modification (m);
}

patch
as_patch (object obj) {
  tmscm p= object_to_tmscm (obj);
  if (!tmscm_is_patch (p)) return patch (array<patch> ());
  return tmscm_to_patch (p);
}

object
tree_to_stree (scheme_tree t) {
  return call ("tree->stree", t);
}

tree
stree_to_tree (object obj) {
  return as_tree (call ("stree->tree", obj));
}

tree
content_to_tree (object obj) {
  return tmscm_to_content (object_to_tmscm (obj));
  // return as_tree (call ("content->tree", obj));
}

object
string_to_object (string s) {
  return call ("string->object", s);
}

string
object_to_string (object obj) {
  return as_string (call ("object->string", obj));
}

object
scheme_cmd (const char* s) {
  return eval ("(lambda () " * string (s) * ")");
}

object
scheme_cmd (string s) {
  return eval ("(lambda () " * s * ")");
}

object
scheme_cmd (object cmd) {
  cmd= cons (cmd, null_object ());
  cmd= cons (null_object (), cmd);
  cmd= cons (eval ("'lambda"), cmd);
  return eval (cmd);
}

/******************************************************************************
 * Conversions to functional objects
 ******************************************************************************/

static inline array<tmscm>
array_lookup (array<object> a) {
  const int    n= N (a);
  array<tmscm> tmscm (n);
  int          i;
  for (i= 0; i < n; i++)
    tmscm[i]= object_to_tmscm (a[i]);
  return tmscm;
}

class object_command_rep : public command_rep {
  object obj;

public:
  object_command_rep (object obj2) : obj (obj2) {}
  void apply () { (void) call_scheme (object_to_tmscm (obj)); }
  void apply (object args) {
    (void) call_scheme (object_to_tmscm (obj),
                        array_lookup (as_array_object (args)));
  }
  tm_ostream& print (tm_ostream& out) { return out << obj; }
};

command
as_command (object obj) {
  return tm_new<object_command_rep> (obj);
}

/******************************************************************************
 * Evaluation and function calls
 ******************************************************************************/

object
eval (const char* expr) {
  return tmscm_to_object (eval_scheme (expr));
}
object
eval (string expr) {
  return tmscm_to_object (eval_scheme (expr));
}
object
eval (object expr) {
  return call ("tm-eval", expr);
}
object
eval_secure (string expr) {
  return eval ("(wrap-eval-secure " * expr * ")");
}
object
eval_file (string name) {
  return tmscm_to_object (eval_scheme_file (name));
}
bool
exec_file (url u) {
  object ret= eval_file (as_string (u));
  return ret != object ("#<unspecified>");
}

object
call (const char* fun) {
  return tmscm_to_object (call_scheme (eval_scheme (fun)));
}
object
call (const char* fun, object a1) {
  return tmscm_to_object (
      call_scheme (eval_scheme (fun), object_to_tmscm (a1)));
}
object
call (const char* fun, object a1, object a2) {
  return tmscm_to_object (call_scheme (eval_scheme (fun), object_to_tmscm (a1),
                                       object_to_tmscm (a2)));
}
object
call (const char* fun, object a1, object a2, object a3) {
  return tmscm_to_object (call_scheme (eval_scheme (fun), object_to_tmscm (a1),
                                       object_to_tmscm (a2),
                                       object_to_tmscm (a3)));
}
object
call (const char* fun, object a1, object a2, object a3, object a4) {
  return tmscm_to_object (call_scheme (
      eval_scheme (fun), object_to_tmscm (a1), object_to_tmscm (a2),
      object_to_tmscm (a3), object_to_tmscm (a4)));
}
object
call (const char* fun, array<object> a) {
  return tmscm_to_object (call_scheme (eval_scheme (fun), array_lookup (a)));
}

object
call (string fun) {
  return tmscm_to_object (call_scheme (eval_scheme (fun)));
}
object
call (string fun, object a1) {
  return tmscm_to_object (
      call_scheme (eval_scheme (fun), object_to_tmscm (a1)));
}
object
call (string fun, object a1, object a2) {
  return tmscm_to_object (call_scheme (eval_scheme (fun), object_to_tmscm (a1),
                                       object_to_tmscm (a2)));
}
object
call (string fun, object a1, object a2, object a3) {
  return tmscm_to_object (call_scheme (eval_scheme (fun), object_to_tmscm (a1),
                                       object_to_tmscm (a2),
                                       object_to_tmscm (a3)));
}
object
call (string fun, object a1, object a2, object a3, object a4) {
  return tmscm_to_object (call_scheme (
      eval_scheme (fun), object_to_tmscm (a1), object_to_tmscm (a2),
      object_to_tmscm (a3), object_to_tmscm (a4)));
}
object
call (string fun, array<object> a) {
  return tmscm_to_object (call_scheme (eval_scheme (fun), array_lookup (a)));
}

object
call (object fun) {
  return tmscm_to_object (call_scheme (object_to_tmscm (fun)));
}
object
call (object fun, object a1) {
  return tmscm_to_object (
      call_scheme (object_to_tmscm (fun), object_to_tmscm (a1)));
}
object
call (object fun, object a1, object a2) {
  return tmscm_to_object (call_scheme (
      object_to_tmscm (fun), object_to_tmscm (a1), object_to_tmscm (a2)));
}
object
call (object fun, object a1, object a2, object a3) {
  return tmscm_to_object (
      call_scheme (object_to_tmscm (fun), object_to_tmscm (a1),
                   object_to_tmscm (a2), object_to_tmscm (a3)));
}
object
call (object fun, object a1, object a2, object a3, object a4) {
  return tmscm_to_object (call_scheme (
      object_to_tmscm (fun), object_to_tmscm (a1), object_to_tmscm (a2),
      object_to_tmscm (a3), object_to_tmscm (a4)));
}
object
call (object fun, array<object> a) {
  return tmscm_to_object (
      call_scheme (object_to_tmscm (fun), array_lookup (a)));
}
